﻿using Castle.DynamicProxy;
using DapperExtensions;
using MemoryStore.Core.DAO.Audit;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XB.Core.DAO.Audit
{
    public class AuditManager
    {
        private static ProxyGenerator _proxyGenerator = new ProxyGenerator();

        public static T CreateProxy<T>(T target) where T : class
        {
            if (target is IAudit)
            {
                return _proxyGenerator.CreateClassProxyWithTarget<T>(target, new PropertyChangeInterceptor());
            }

            return target;
        }

        public static void AppendAudit<T>(IDbConnection dc, T model, EnumAuditAction auditAction, AuditOperator oper = null, IDbTransaction transaction = null) where T : class
        {
            if (!(model is IAudit))
            {
                return;
            }

            var modifiedList = ((IAudit)model).GetAuditHandler().GetModifiedList();
            if (modifiedList == null || modifiedList.Count == 0)
            {
                return;
            }

            var auditLog = BuildAuditLog<T>(oper, model, auditAction);
            if (auditAction == EnumAuditAction.Delete)
            {
                dc.Insert(auditLog, transaction);
                return;
            }

            Action<AuditLog, IDbConnection, IDbTransaction> InsertAction = (audit, connection, trans) =>
            {
                connection.Insert(audit, trans);
                var auditLogItems = BuildAuditLogItems(model, audit.Id);
                if (auditLogItems != null && auditLogItems.Count > 0)
                {
                    connection.Insert<AuditLogItem>(auditLogItems, trans);
                }
            };

            if (transaction != null)
            {
                InsertAction(auditLog, dc, transaction);
            }
            else
            {
                using (var trans = dc.BeginTransaction())
                {
                    InsertAction(auditLog, dc, trans);
                    trans.Commit();
                }
            }
        }

        private static AuditLog BuildAuditLog<T>(AuditOperator oper, T model, EnumAuditAction auditAction) where T : class
        {
            var auditLog = new AuditLog() { EnumAuditAction = auditAction };
            if (oper != null)
            {
                auditLog.EditorName = oper.OperatorName;
                auditLog.EditorId = oper.OperatorId;
                auditLog.MethodName = oper.MethodName;
            }

            var classMapper = DapperExtensions.DapperExtensions.GetMap<T>();
            auditLog.EntityName = classMapper.SchemaName;

            var key = classMapper.Properties.FirstOrDefault(p => p.KeyType != DapperExtensions.Mapper.KeyType.NotAKey);
            if (key != null)
            {
                auditLog.EntityKeyName = key.Name;

                var keyValue = key.PropertyInfo.GetValue(model);
                auditLog.EntityKeyValue = keyValue != null ? keyValue.ToString() : null;
            }

            return auditLog;
        }

        private static List<AuditLogItem> BuildAuditLogItems<T>(T model, int auditLogId) where T : class
        {
            var res = new List<AuditLogItem>();

            var auditHandler = ((IAudit)(model)).GetAuditHandler();
            if (auditHandler == null)
            {
                return null;
            }

            return auditHandler.GetModifiedList().Select(m => new AuditLogItem
            {
                AuditLogId = auditLogId,
                NewValue = m.NewValue,
                OldValue = m.OldValue,
                PropertyName = m.PropertyName
            }).ToList();
        }
    }
}
